package com.piece.core.message.client.impl;

import com.aliyuncs.AcsRequest;
import com.aliyuncs.AcsResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySendDetailsResponse;
import com.aliyuncs.dysmsapi.model.v20170525.QuerySmsTemplateRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.piece.core.framework.exception.ErrorCode;
import com.piece.core.framework.support.bootstrap.DataTable;
import com.piece.core.framework.support.convert.Convert;
import com.piece.core.framework.support.response.AjaxResponse;
import com.piece.core.framework.util.object.BeanUtil;
import com.piece.core.framework.util.string.JsonUtil;
import com.piece.core.framework.util.string.StringUtil;
import com.piece.core.framework.util.time.DateUtil;
import com.piece.core.log.client.LogFactory;
import com.piece.core.message.client.dto.SmsReceiveRespDTO;
import com.piece.core.message.client.dto.SmsSendRespDTO;
import com.piece.core.message.client.dto.SmsTemplateRespDTO;
import com.piece.core.message.constant.SmsErrorCodeConstants;
import com.piece.core.message.enums.AuditStatusType;
import com.piece.core.message.properties.SmsProperties;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.LinkedMap;
import org.springframework.util.Assert;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 阿里短信客户端的实现类
 */
@Slf4j
public class AliyunSmsClient extends AbstractSmsClient {

    /**
     * REGION, 使用杭州
     */
    private static final String ENDPOINT = "cn-hangzhou";

    /**
     * 阿里云客户端
     */
    private volatile IAcsClient client;

    public AliyunSmsClient(String applicationName, SmsProperties properties, LogFactory auditLogFactory) {
        super(applicationName, properties, auditLogFactory);
        Assert.notEmpty(new Object[]{ properties.getApiKey() }, "apiKey 不能为空");
        Assert.notEmpty(new Object[]{ properties.getApiSecret() }, "apiSecret 不能为空");
    }

    @Override
    protected void doInit() {
        IClientProfile profile = DefaultProfile.getProfile(ENDPOINT, properties.getApiKey(), properties.getApiSecret());
        client = new DefaultAcsClient(profile);
    }

    @Override
    protected AjaxResponse doSendSms(String sendLogId, String mobile, String apiTemplateId, LinkedMap templateParams) {
        // 构建参数
        SendSmsRequest request = new SendSmsRequest();
        request.setPhoneNumbers(mobile);
        request.setSignName(properties.getSignature());
        request.setTemplateCode(apiTemplateId);
        request.setTemplateParam(JsonUtil.mapToString(templateParams));
        request.setOutId(sendLogId);
        // 执行请求
        return invoke(request, response -> SmsSendRespDTO.builder()
                .bizId(response.getBizId())
                .outId(sendLogId)
                .content(JsonUtil.mapToString(templateParams))
                .receiveDate(DateUtil.formatDate(new Date(), DateUtil.FORMAT_YYYY_MM_DD_HH_MM_SS))
                .build());
    }

    @Override
    protected List<SmsReceiveRespDTO> doParseSmsReceiveStatus(String text) {
        List<SmsReceiveStatus> statuses = JsonUtil.toList(text, SmsReceiveStatus.class);
        return statuses.stream().map(status -> {
            return SmsReceiveRespDTO.builder()
                    .outId(status.getOutId())
                    .success(status.getSuccess())
                    .errorCode(status.getErrCode())
                    .errorMsg(status.getErrMsg())
                    .mobile(status.getPhoneNumber())
                    .receiveTime(status.getReportTime())
                    .bizId(status.getBizId())
                    .build();
        }).collect(Collectors.toList());
    }

    @Override
    protected AjaxResponse doGetSmsTemplate(String apiTemplateId) {
        // 构建参数
        QuerySmsTemplateRequest request = new QuerySmsTemplateRequest();
        request.setTemplateCode(apiTemplateId);
        // 执行请求
        return invoke(request, response -> {
            return SmsTemplateRespDTO.builder()
                    .id(response.getTemplateCode())
                    .content(response.getTemplateContent())
                    .auditStatus(convertSmsTemplateAuditStatus(response.getTemplateStatus()))
                    .auditReason(response.getReason())
                    .build();
        });
    }

    private Integer convertSmsTemplateAuditStatus(Integer templateStatus) {
        switch (templateStatus) {
            case 0: return AuditStatusType.CHECKING.getStatus();
            case 1: return AuditStatusType.SUCCESS.getStatus();
            case 2: return AuditStatusType.FAIL.getStatus();
            default: throw new IllegalArgumentException(String.format("未知审核状态(%d)", templateStatus));
        }
    }

    @Override
    public AjaxResponse queryDetail(String mobile, String bizId, int pageIndex, int pageSize) {
        //组装请求对象
        QuerySendDetailsRequest request = new QuerySendDetailsRequest();
        //必填-号码
        request.setPhoneNumber(mobile);
        //可选-流水号
        request.setBizId(bizId);
        //必填-发送日期 支持30天内记录查询，格式yyyyMMdd
        SimpleDateFormat ft = new SimpleDateFormat("yyyyMMdd");
        request.setSendDate(ft.format(new Date()));
        //必填-当前页码从1开始计数
        request.setCurrentPage((long) pageIndex);
        //必填-页大小
        request.setPageSize((long) pageSize);

        // 执行请求
        return invoke(request, response -> {
            DataTable<SmsSendRespDTO> dataTable = new DataTable();
            dataTable.setStart(pageIndex);
            dataTable.setLength(pageSize);
            dataTable.setRecordsTotal(Convert.toInt(response.getTotalCount(), 0));
            List<QuerySendDetailsResponse.SmsSendDetailDTO> list = response.getSmsSendDetailDTOs();
            List<SmsSendRespDTO> data = list.stream().map(dto -> SmsSendRespDTO.builder()
                        .outId(dto.getOutId())
                        .mobile(dto.getPhoneNum())
                        .templateCode(dto.getTemplateCode())
                        .content(dto.getContent())
                        .errorCode(dto.getErrCode())
                        .sendDate(dto.getSendDate())
                        .receiveDate(dto.getReceiveDate())
                        .build()
            ).collect(Collectors.toList());
            dataTable.setData(data);
            return dataTable;
        });
    }

    private <T extends AcsResponse, R> AjaxResponse invoke(AcsRequest<T> request, Function<T, R> responseConsumer) {
        try {
            T sendResult = client.getAcsResponse(request);
            String code = (String) BeanUtil.getFieldValue(sendResult, "code");
            String message = (String) BeanUtil.getFieldValue(sendResult, "message");
            String requestId = (String) BeanUtil.getFieldValue(sendResult, "requestId");
            // 解析结果
            R data = null;
            if (Objects.equals(code, "OK")) {
                data = responseConsumer.apply(sendResult);
            }
            // 拼接结果
            return AjaxResponse.result(requestId, convert(code, message), data);
        } catch (ClientException e) {
            return AjaxResponse.result(e.getRequestId(), convert(e.getErrCode(), e.getErrMsg()), formatResultMsg(e));
        }
    }

    private ErrorCode convert(String code, String msg) {
        switch (code) {
            case "OK": return ErrorCode.SUCCESS;
            case "isv.ACCOUNT_NOT_EXISTS":
            case "isv.ACCOUNT_ABNORMAL":
            case "MissingAccessKeyId": return SmsErrorCodeConstants.SMS_ACCOUNT_INVALID;
            case "isp.RAM_PERMISSION_DENY": return SmsErrorCodeConstants.SMS_PERMISSION_DENY;
            case "isv.INVALID_JSON_PARAM":
            case "isv.INVALID_PARAMETERS": return SmsErrorCodeConstants.SMS_API_PARAM_ERROR;
            case "isv.BUSINESS_LIMIT_CONTROL": return SmsErrorCodeConstants.SMS_SEND_BUSINESS_LIMIT_CONTROL;
            case "isv.DAY_LIMIT_CONTROL": return SmsErrorCodeConstants.SMS_SEND_DAY_LIMIT_CONTROL;
            case "isv.SMS_CONTENT_ILLEGAL": return SmsErrorCodeConstants.SMS_SEND_CONTENT_INVALID;
            case "isv.SMS_TEMPLATE_ILLEGAL": return SmsErrorCodeConstants.SMS_TEMPLATE_INVALID;
            case "isv.SMS_SIGNATURE_ILLEGAL":
            case "isv.SIGN_NAME_ILLEGAL":
            case "isv.SMS_SIGN_ILLEGAL": return SmsErrorCodeConstants.SMS_SIGN_INVALID;
            case "isv.AMOUNT_NOT_ENOUGH":
            case "isv.OUT_OF_SERVICE": return SmsErrorCodeConstants.SMS_ACCOUNT_MONEY_NOT_ENOUGH;
            case "isv.MOBILE_NUMBER_ILLEGAL": return SmsErrorCodeConstants.SMS_MOBILE_INVALID;
            case "isv.TEMPLATE_MISSING_PARAMETERS": return SmsErrorCodeConstants.SMS_TEMPLATE_PARAM_ERROR;
        }

        return new ErrorCode(500, msg);
    }

    private static String formatResultMsg(ClientException e) {
        if (StringUtil.isEmpty(e.getErrorDescription())) {
            return e.getErrMsg();
        }
        return e.getErrMsg() + " => " + e.getErrorDescription();
    }

    /**
     * 短信接收状态
     */
    @Data
    public static class SmsReceiveStatus {
        /**
         * 手机号
         */
        @JsonProperty("phone_number")
        private String phoneNumber;
        /**
         * 发送时间
         */
        @JsonProperty("send_time")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date sendTime;
        /**
         * 状态报告时间
         */
        @JsonProperty("report_time")
        @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
        private Date reportTime;
        /**
         * 是否接收成功
         */
        private Boolean success;
        /**
         * 状态报告说明
         */
        @JsonProperty("err_msg")
        private String errMsg;
        /**
         * 状态报告编码
         */
        @JsonProperty("err_code")
        private String errCode;
        /**
         * 发送序列号
         */
        @JsonProperty("biz_id")
        private String bizId;
        /**
         * 用户序列号
         */
        @JsonProperty("out_id")
        private String outId;
        /**
         * 短信长度，例如说 1、2、3
         *
         * 140 字节算一条短信，短信长度超过 140 字节时会拆分成多条短信发送
         */
        @JsonProperty("sms_size")
        private Integer smsSize;
    }
}